﻿using FySystem.Converter;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FySystem.Net.IOT.CommandSender
{
    internal class SendUtils<T>
    {
        internal ConcurrentQueue<CmdInfo<T>> SendQueue = new ConcurrentQueue<CmdInfo<T>>();

        internal SendUtils()
        {
            try
            {
                Task task = new Task(ProcessFun);
                task.Start();
            }
            catch (Exception)
            {
                throw;
            }
        }

        private void ProcessFun()
        {
            while (true)
            {
                try
                {
                    CmdInfo<T> cmdInfo = null;
                    if (SendQueue.TryDequeue(out cmdInfo))
                    {
                        if (cmdInfo.IsComplete == true)
                            continue;
                        if (cmdInfo.DeviceR.DeviceSocket == null)
                            continue;
                        else if (cmdInfo.DeviceR.DeviceSocket.Connected == false)
                            continue;

                        //重发次数超过上限则丢弃
                        if (cmdInfo.SendCnt > cmdInfo.Repeat)
                        {
                            cmdInfo.DeviceR.SendComplete = true;        //发送完成标记设置为true，否则无法发送下一条指令
                            continue;
                        }

                        DateTime now = DateTime.Now;
                        bool canSend = false;
                        if (cmdInfo.DeviceR.LastCmdSendTime == null)
                        {
                            canSend = true;         //说明是第一次发送指令，不做限制直接发送
                            UpdateCommandInfo(cmdInfo, ref now);
                        }
                        else
                        {
                            if (cmdInfo.SendCnt <= 0)
                            {
                                //如果这条指令不是重发
                                if ((now - cmdInfo.DeviceR.LastCmdSendTime.Value).TotalMilliseconds >= cmdInfo.SplitTime
                                        && cmdInfo.DeviceR.SendComplete == true)
                                {
                                    if (cmdInfo.DeviceR.CurrentCommand != null)
                                    {
                                        //以下判断是为了保证发送的指令是按照调用SendManager.Send()的顺序来的
                                        if (cmdInfo.DeviceR.CurrentCommand.NextCommand == cmdInfo)
                                        {
                                            canSend = true;
                                            UpdateCommandInfo(cmdInfo, ref now);
                                        }
                                    }
                                    else
                                    {
                                        canSend = true;
                                        UpdateCommandInfo(cmdInfo, ref now);
                                    }
                                }
                            }
                            else
                            {
                                //如果这条指令是重发
                                if ((now - cmdInfo.DeviceR.LastCmdSendTime.Value).TotalMilliseconds >= cmdInfo.Timeout)
                                {
                                    if (cmdInfo.SendCnt < cmdInfo.Repeat)
                                    {
                                        canSend = true;
                                        cmdInfo.DeviceR.LastCmdSendTime = now;
                                    }
                                    else
                                        cmdInfo.SendCnt++;
                                }
                            }
                        }

                        if (canSend == false)
                        {
                            SendQueue.Enqueue(cmdInfo);
                            Thread.Sleep(1);

                            continue;
                        }

                        //发送次数加一
                        cmdInfo.SendCnt++;

                        //异步发送
                        cmdInfo.DeviceR.DeviceSocket.BeginSend(cmdInfo.Command, 0, cmdInfo.Command.Length,
                            System.Net.Sockets.SocketFlags.None, SendCallback, cmdInfo);

                        if (cmdInfo.NeedReply)
                        {
                            SendQueue.Enqueue(cmdInfo);
                        }
                    }
                    else
                    {
                        //如果没数据，休眠
                        Thread.Sleep(100);
                    }

                    Thread.Sleep(0);                //据说这样可以将CPU资源先交给其它线程去处理别的事
                }
                catch (Exception ex)
                {
                    FyLogContext.Write(ex.Message);
                }
            }
        }

        /// <summary>
        /// 更新发送信息
        /// </summary>
        /// <param name="cmdInfo"></param>
        /// <param name="now"></param>
        private void UpdateCommandInfo(CmdInfo<T> cmdInfo, ref DateTime now)
        {
            try
            {
                //更新发送时间
                cmdInfo.DeviceR.LastCmdSendTime = now;

                //设置当前正在发送的指令
                cmdInfo.DeviceR.CurrentCommand = cmdInfo;

                //如果需要回复，则将是否发送完成的标记改为false，直到收到对应的数据后再改为true，如果不需要回复，则直接为true
                cmdInfo.DeviceR.SendComplete = cmdInfo.NeedReply == true ? false : true;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 消息发送回调
        /// </summary>
        /// <param name="ar"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                CmdInfo<T> cmd = (CmdInfo<T>)ar.AsyncState;
                string hex = NumberConversion.BytesToHexString(cmd.Command, 0, cmd.Command.Length, false);
                if (cmd.SendCnt == 1)
                    FyLogContext.Write($"发送#{cmd.DeviceR.GatewaySN}#{hex}");
                else
                    FyLogContext.Write($"超时重发第{cmd.SendCnt}次#{cmd.DeviceR.GatewaySN}#{hex}");
            }
            catch (Exception ex)
            {
                FyLogContext.Write(ex.Message);
            }
        }
    }
}
